Check for mouse leave/enter canvas for more robust drag event handling / responsiveness
Ideas for interactive drag & resize:
Reduce latencies in mouse/transform responsiveness:
decompose()
function from here: https://github.com/paperjs/paper.js/blob/master/src/basic/Matrix.js
In [1]:
from __future__ import print_function, unicode_literals, division, absolute_import
from widget_canvas import CanvasImage
from widget_canvas import image
import toyplot as tp
import IPython
from ipywidgets import widgets
In [2]:
# Load two images.
data_A = image.read('images/Whippet.jpg')
data_B = image.read('images/Doberman.jpg')
The Canvas Element
does not do a good job of keeping track of its own transformation matrix. Or at least the API does not make it easily retrievable once it's been set. I wrote a Python helper class based on an earlier JavaScript implementation by Simon Sarris: transform.js.
Canvas Element and image transformations:
Great info about specifying the source rectangle, the destination rectangle, and how the transform is applied: link
http://developers.whatwg.org/the-canvas-element.html#transformations
In [3]:
from widget_canvas import transform
In [ ]:
T = transform.Transform()
In [ ]:
T.reset()
In [ ]:
G = T.translate((4,4)).rotate(45/180.*np.pi)
In [ ]:
In [ ]:
G.invert()
In [ ]:
palette = tp.color.brewer('Set1', 3)
In [ ]:
points = [[0.0, 0.0],
[1.0, 0.1],
[2.0, 3.0],
[1.5, 2.0],
[1.0, 3.5]]
points = np.asarray(points)
In [ ]:
IPython.display.display(palette)
canvas = tp.Canvas(width=500, height=400)
axes = canvas.axes()
m = 'o'
x = points[:, 0].tolist()
y = points[:, 1].tolist()
c = palette.color(0)
mark = axes.scatterplot(x, y, marker=m, color=c, size=50)
In [ ]:
In [ ]:
In [ ]:
1/0
In [ ]:
import display_mouse_events
wid_image = display_mouse_events.display(data_A)
# Build a few helper widgets.
wid_butt = IPython.html.widgets.ButtonWidget(description='Reset Transform')
IPython.display.display(wid_butt)
In [ ]:
# Build event handler for the button I just added in the cell above.
def handle_reset(widget_butt):
wid_image.transform.reset()
# Attach event handler to the button.
wid_butt.on_click(handle_reset)
In [ ]:
IPython.display.display(wid_image.transform)
In [ ]:
print(0)
wid_image.transform.scale(1.2).scale(5).rotate(15)
print(1)
In [ ]:
IPython.display.display(wid_image.transform)
In [ ]:
1/0
In [ ]:
import time
T = canvas.transform.Transform()
def update_transform(T):
wid_image._transform = T.values
# IPython.display.clear_output(True)
# IPython.display.display(T)
def handle_slide_scale(name_trait, value_old, value_new):
X, Y = wid_image.mouse_xy
T.translate(-X, -Y)
T.scale(value_new/value_old)
T.translate(X, Y)
update_transform(T)
# def handle_A(wid):
# T.scale(1./1.1)
# update_transform(T)
# def handle_B(wid):
# T.scale(1.1)
# update_transform(T)
def handle_scroll(widget, ev):
tick = ev['deltaY']
factor = 1.1
if tick == 0:
# raise Exception()
# print('tick == 0, do nothing')
return
if tick > 0:
scl = factor
else:
scl = 1./factor
px, py = ev['canvas_xy']
Q = T.copy()
Q.invert()
px, py = Q.transform_point(px, py)
T.translate(px, py).scale(scl).translate(-px, -py)
update_transform(T)
def handle_drag(widget, ev):
dx, dy = ev['drag_delta_xy']
# D = T.copy()
# qx, qy = Q.invert().transform_point(dx, dy)
# qx, qy = T.transform_point(dx, dy)
# T.translate(dx, dy, update=True) #.scale(scl).translate(px, py)
Q = T.copy()
Q.invert()
p0x, p0y = Q.m13, Q.m23 # Q.transform_point(0, 0)
pdx, pdy = Q.transform_point(dx, dy)
# print(dx, dy, px, py)
T.translate(pdx-p0x, pdy-p0y)
update_transform(T)
# wid_slide.on_trait_change(handle_slide_scale, name=str('value'))
# wid_butt_A.on_click(handle_A)
wid_butt_C.on_click(handle_C)
wid_image.on_mouse_wheel(handle_scroll)
wid_image.on_mouse_drag(handle_drag)
In [ ]: